

#ifndef LOD_SPH_CAMERA_H
#define LOD_SPH_CAMERA_H

#include "base/point3.h"
#include "base/vector3.h"
#include "types.h"
#include "tools.h"

#include <assert.h>
#include <GL/glu.h>
#include <cmath>
#include <math.h>

//! Camera based on spherical coordinate system
class sph_camera
{

    public:

    sph_camera(vector3<co_type> Surf_v1, vector3<co_type> Surf_v2,
               point3<co_type> reference_point,
               point3<co_type> Eye)
     : surf_v1(Surf_v1),
       surf_v2(Surf_v2),
       ref_pt(reference_point)
    {
        // make the reference point the center of the coordinate system
        eye.x = Eye.x - reference_point.x;
        eye.y = Eye.y - reference_point.y;
        eye.z = Eye.z - reference_point.z;
        distance = norm(eye); 
        assert(distance>0);
        
        // the surface given by two vectors includes the equator
        // (phi rotates around a vector perpendicular to that surface)
        // todo: at the moment the surface is fixed to ([1 0 0], [0 1 0])
        phi = std::atan2(eye.y, eye.x);
        theta = std::asin((eye.z) / distance);
        surf_v1 = vector3<co_type>(1,0,0);
        surf_v2 = vector3<co_type>(0,1,0);

        //todo
        up = vector3<co_type>(0,0,1);
        
        //todo
        eye = Eye;
        //cout<<Eye.x<<"   "<<Eye.y<<"  "<<Eye.z<<endl;
        //calc_cartesian_eye_pos();
        //cout<<eye.x<<"   "<<eye.y<<"  "<<eye.z<<endl;
    }

    //! Place the camera at the (previously) specified position
    /*! The method depends on the current transformation matrix */
    void place()
    {
        //todo delete
        //cout<<eye.x<<"   "<<eye.y<<"  "<<eye.z<<endl;
        //cout<<ref_pt.x<<"   "<<ref_pt.y<<"  "<<ref_pt.z<<endl;
        gluLookAt(eye.x, eye.y, eye.z,
                ref_pt.x, ref_pt.y, ref_pt.z,
                up.x, up.y, up.z);
    }

    /*! Depends on phi */
    void change_longitude(co_type angle_radians)
    {
        int m;
        phi += angle_radians;
        m = (int)(phi/(2*M_PI));
        phi -= m*2*M_PI;

        calc_cartesian_eye_pos();
    }

    /*! Depends on theta */
    bool change_latitude(co_type angle_radians)
    {
        co_type ntheta = theta + angle_radians;
        if(ntheta >= -M_PI/2  &&  ntheta <= M_PI/2) {
            theta = ntheta;
            calc_cartesian_eye_pos();
            return true;
        }
        else
            return false;
    }

    bool change_distance(co_type value)
    {
        bool ret;
        co_type newdist = distance + value;
        if(newdist>0) {
            distance = newdist;
            calc_cartesian_eye_pos();
            ret = true;
        }
        else
            ret = false;
        
        return ret;
    }

    point3<co_type> get_eye_pos()
    {
        return eye;
    }

    point3<co_type> get_ref_pt()
    {
        return ref_pt;
    }
               

    private:
    void calc_cartesian_eye_pos()
    {
        eye.x = distance * std::cos(theta) * std::cos(phi);
        eye.y = distance * std::cos(theta) * std::sin(phi);
        eye.z = distance * std::sin(theta);
        
        eye.x += ref_pt.x;
        eye.y += ref_pt.y;
        eye.z += ref_pt.z;
    }
    
    vector3<co_type> surf_v1;
    vector3<co_type> surf_v2;
    vector3<co_type> up;
    co_type distance;
    co_type phi, theta;

    point3<co_type> ref_pt;
    point3<co_type> eye;
};



#endif // LOD_SPH_CAMERA_H
